#include <iostream>
#include "Poco/Data/Session.h"
#include "Poco/Data/Statement.h"
#include "Poco/Data/RecordSet.h"
#include "Poco/Data/SQLite/Connector.h"
#include "SqliteDB.h"

using namespace SystemTool;

// Construct the db
SqliteDB::SqliteDB() : m_pSession(nullptr)
{
    Poco::Data::SQLite::Connector::registerConnector();
}

// Destruct the db
SqliteDB::~SqliteDB()
{
    Disconnect();
    Poco::Data::SQLite::Connector::unregisterConnector();
}

// Connect to db with tiemout
bool SqliteDB::ConnectTo(std::string strDbFilePath)
{
    try
    {
        m_pSession = new Poco::Data::Session(Poco::Data::SQLite::Connector::KEY, strDbFilePath);
        if (m_pSession == nullptr)
        {
            return false;
        }
        return true;
    }
    catch (const Poco::Exception &e)
    {
        std::cerr << "Err happened:" << e.displayText() << std::endl;
    }

    return false;
}

// Disconnect from db
void SqliteDB::Disconnect()
{
    if(m_pSession)
    {
        delete (Poco::Data::Session*)m_pSession;
        m_pSession = nullptr;
    }
}

// Is connected to db
bool SqliteDB::IsConnected()
{
    if(m_pSession == nullptr)
    {
        return false;
    }

    return ((Poco::Data::Session*)m_pSession)->isConnected();
}

// Reconnect to the db
void SqliteDB::Reconnect()
{
    if(m_pSession == nullptr)
    {
        return;
    }

    try
    {
        ((Poco::Data::Session *)m_pSession)->reconnect();
    }
    catch (const Poco::Exception &e)
    {
        std::cerr << "Err happened:" << e.displayText() << std::endl;
    }
}

// Excute the single sql(add/delete/update/search)
bool SqliteDB::ExcuteSql(std::string strSql, std::vector<std::vector<std::string>> *pSearchRetTable)
{
    if(strSql.empty())
    {
        return false;
    }

    if (m_pSession == nullptr)
    {
        return false;
    }

    try
    {
        Poco::Data::Session &ss = *((Poco::Data::Session *)m_pSession);
        if(!pSearchRetTable)
        {
            ss << strSql, Poco::Data::Keywords::now;
        }
        else
        {
            Poco::Data::RecordSet rset(ss, strSql);

            for (size_t index = 0; index < rset.rowCount(); index++)
            {
                Poco::Data::Row& CurRow = rset.row(index);

                std::vector<std::string> vTable;
                for (size_t index = 0; index < rset.columnCount(); index++)
                {
                    vTable.push_back(CurRow[index].toString());
                }

                pSearchRetTable->push_back(vTable);
            }
        }
    }
    catch (const Poco::Exception &e)
    {
        std::cerr << "Err happened:" << e.displayText() << std::endl;
    }

    return false;
}

// Excute the add/delete/update by transaction
bool SqliteDB::ExcuteByTrans(std::vector<std::string> &vSqlTable)
{
    if(vSqlTable.empty())
    {
        return false;
    }

    if (m_pSession == nullptr)
    {
        return false;
    }

    Poco::Data::Session &ss = *((Poco::Data::Session *)m_pSession);
    try
    {
        ss.begin();
        for (size_t index = 0; index < vSqlTable.size(); index++)
        {
            ss << vSqlTable[index], Poco::Data::Keywords::now;
        }
        ss.commit();
    }
    catch (const Poco::Exception &e)
    {
        ss.rollback();
        std::cerr << "Err happened:" << e.displayText() << std::endl;
    }

    return false;
}

// Excute the searching by transaction
bool SqliteDB::SearchByTrans(std::map<std::string, std::vector<std::string>> &SqlMap)
{
    if (SqlMap.empty())
    {
        return false;
    }

    if (m_pSession == nullptr)
    {
        return false;
    }

    Poco::Data::Session &ss = *((Poco::Data::Session *)m_pSession);
    try
    {
        ss.begin();

        std::map<std::string, std::vector<std::string>>::iterator Iter = SqlMap.begin();
        while (Iter != SqlMap.end())
        {
            std::string strSql = Iter->first;
            std::vector<std::string> &vTable = Iter->second;

            Poco::Data::RecordSet rset(ss, strSql);
            for (size_t index = 0; index < rset.rowCount(); index++)
            {
                Poco::Data::Row &CurRow = rset.row(index);

                for (size_t index = 0; index < rset.columnCount(); index++)
                {
                    vTable.push_back(CurRow[index].toString());
                }
            }

            ++Iter;
        }

        ss.commit();
    }
    catch (const Poco::Exception &e)
    {
        ss.rollback();
        std::cerr << "Err happened:" << e.displayText() << std::endl;
    }

    return false;
}
